use crate::c24::{Add, Div, F32Op, Mul, Sub};

pub struct C23Data {
    pub a: f32,
    pub b: f32,
    pub c: f32,
    pub d: f32,
}

pub fn find24(data: &C23Data) -> Vec<String> {
    let ops: [F32Op; 4] = [Box::new(Add), Box::new(Sub), Box::new(Mul), Box::new(Div)];

    let mut vec = Vec::new();

    for ab in ops.iter() {
        for bc in ops.iter() {
            for cd in ops.iter() {
                if Some(24.0) == calc(data, ab, bc, cd) {
                    let s = format!(
                        "{}{}{}{}{}{}{}",
                        data.a,
                        ab.name(),
                        data.b,
                        bc.name(),
                        data.c,
                        cd.name(),
                        data.d
                    );
                    vec.push(s);
                };
            }
        }
    }

    vec
}

fn calc(data: &C23Data, op_ab: &F32Op, op_bc: &F32Op, op_cd: &F32Op) -> Option<f32> {
    if op_ab.is_primary() {
        let ab = op_ab.exec(data.a, data.b)?;
        if op_bc.is_primary() {
            let abc = op_bc.exec(ab, data.c)?;
            return op_cd.exec(abc, data.d);
        } else {
            let cd = op_cd.exec(data.c, data.d)?;
            return op_bc.exec(ab, cd);
        }
    } else {
        if op_bc.is_primary() {
            let bc = op_bc.exec(data.b, data.c)?;
            let bcd = op_cd.exec(bc, data.d)?;
            return op_ab.exec(data.a, bcd);
        } else {
            let ab = op_ab.exec(data.a, data.b)?;
            let cd = op_cd.exec(data.c, data.d)?;
            return op_bc.exec(ab, cd);
        }
    }
}
